﻿using System;
using Glue;
using Xunit;

namespace GlueTests.MapperTests
{
    public class When_not_supplying_toObject
    {
        public class PersonWithConstructor
        {
            public String TheText { get; set; }
            public NestedObject NestedObject { get; set; } 
            
            public PersonWithConstructor(String aParameter) {TheText = aParameter;}
        }

        public class NestedObject { public String TheText { get; set; } }

        public class AnotherPerson
        {
            public String TheText { get; set; }
            public AnotherNestedObject NestedObject { get; set; } 
        }
        
        public class AnotherNestedObject { public String TheText { get; set; } }

        [Fact]
        public void Should_map_towards_left_when_creator_is_supplied_via_constructor()
        {
            var mapping = new Mapping<PersonWithConstructor, AnotherPerson>(x=> new PersonWithConstructor("justAText"),y=>new AnotherPerson() );
            mapping.Relate(x=>x.TheText,y=>y.TheText);
            var anotherPerson = new AnotherPerson{TheText = "abc"};
            var person = mapping.Map(anotherPerson);
            Assert.Equal("abc",person.TheText);
        }

        [Fact]
        public void Should_map_towards_right_when_creator_is_supplied_via_constructor()
        {
            var mapping = new Mapping<AnotherPerson,PersonWithConstructor>(x => new AnotherPerson(),y => new PersonWithConstructor("justAText"));
            mapping.Relate(x => x.TheText, y => y.TheText);
            var anotherPerson = new AnotherPerson { TheText = "abc" };
            var person = mapping.Map(anotherPerson);
            Assert.Equal("abc", person.TheText);
        }

        [Fact]
        public void Should_throw_meaningful_exception_when_creator_does_not_exist_and_object_has_no_parameterless_constructor_towards_left()
        {
            var mapping = new Mapping<PersonWithConstructor, AnotherPerson>();
            mapping.Relate(x => x.TheText, y => y.TheText);
            var anotherPerson = new AnotherPerson { TheText = "abc" };

            Assert.Throws(typeof(GlueException), ()=> mapping.Map(anotherPerson));
        }


        [Fact]
        public void Should_throw_meaningful_exception_when_creator_does_not_exist_and_object_has_no_parameterless_constructor_towards_right()
        {
            var mapping = new Mapping<AnotherPerson, PersonWithConstructor>();
            mapping.Relate(x => x.TheText, y => y.TheText);
            var anotherPerson = new AnotherPerson { TheText = "abc" };

            Assert.Throws(typeof(GlueException), () => mapping.Map(anotherPerson));
        }

        [Fact]
        public void Should_map_towards_left_when_creator_was_added()
        {
            var mapping = new Mapping<PersonWithConstructor, AnotherPerson>();
            mapping.AddCreator(typeof(PersonWithConstructor), x=>new PersonWithConstructor("TheText"));
            mapping.Relate(x => x.TheText, y => y.TheText);
            var anotherPerson = new AnotherPerson { TheText = "abc" };
            var person = mapping.Map(anotherPerson);
            Assert.Equal("abc", person.TheText);
        }

        [Fact]
        public void Should_map_towards_right_when_creator_was_added()
        {
            var mapping = new Mapping<AnotherPerson, PersonWithConstructor>();
            mapping.AddCreator(typeof(PersonWithConstructor), x => new PersonWithConstructor("justAText"));
            mapping.Relate(x => x.TheText, y => y.TheText);
            var anotherPerson = new AnotherPerson { TheText = "abc" };
            var person = mapping.Map(anotherPerson);
            Assert.Equal("abc", person.TheText);
        }

        [Fact]
        public void Should_create_object_towards_right_if_no_creator_is_present_and_type_has_parameterless_constructor()
        {
            var mapping = new Mapping<PersonWithConstructor,AnotherPerson>();
            mapping.Relate(x => x.TheText, y => y.TheText);
            var person = new PersonWithConstructor("abc");
            var anotherPerson = mapping.Map(person);
            Assert.Equal("abc", anotherPerson.TheText);
        }

        [Fact]
        public void Should_create_object_towards_left_if_no_creator_is_present_and_type_has_parameterless_constructor()
        {
            var mapping = new Mapping<AnotherPerson, PersonWithConstructor>();
            mapping.Relate(x => x.TheText, y => y.TheText);
            var person = new PersonWithConstructor("abc");
            var anotherPerson = mapping.Map(person);
            Assert.Equal("abc", anotherPerson.TheText);
        }

        [Fact]
        public void Should_map_towards_right_when_creator_was_added_for_nested_object()
        {
            // Remember: should not use creators if not using that Map-method
            var mapping = new Mapping<AnotherPerson, PersonWithConstructor>();
            mapping.AddCreator(typeof(PersonWithConstructor), x => new PersonWithConstructor("justAText"));
            mapping.AddCreator(typeof(NestedObject),x=>new NestedObject());
            mapping.Relate(x => x.NestedObject.TheText, y => y.NestedObject.TheText);
            var anotherPerson = new AnotherPerson { NestedObject = new AnotherNestedObject{TheText = "abc"} };
            var person = mapping.Map(anotherPerson);
            Assert.Equal("abc", person.NestedObject.TheText);
        }
    }
}
